package blue.jdbc.internal.core.engine;

import blue.base.core.path.ClassScanner;
import blue.jdbc.core.engine.Jdbc;
import blue.jdbc.core.engine.JdbcBuilder;
import blue.jdbc.core.engine.JdbcOperation;
import blue.jdbc.core.options.DataSourceOptions;
import blue.jdbc.internal.core.dialect.DetectDialect;
import blue.jdbc.internal.core.executor.ConnectionFactory;
import blue.jdbc.internal.core.executor.DataSourceFactory;
import blue.jdbc.internal.core.executor.mapper.RowMapperFactory;
import blue.jdbc.internal.core.executor.metadata.TableChecker;
import blue.jdbc.internal.core.parser.ParserFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Jin Zheng
 * @since 1.0 2021-11-02
 */
public class DefaultJdbcBuilder implements JdbcBuilder {
	private static Logger logger = LoggerFactory.getLogger(DefaultJdbcBuilder.class);

	private DataSourceOptions dataSourceOptions;
	private DataSourceFactory dataSourceFactory;
	private DefaultJdbcOperation jdbcOperation;
	private ComponentFactory componentFactory;
	private ParserFactory parserFactory;
	private final List<Class<?>> clazzList = new ArrayList<>();
	private final List<String> pkgList = new ArrayList<>();

	public DefaultJdbcBuilder() {
	}

	@Override
	public Jdbc build() {
		this.dataSourceFactory = new DataSourceFactory(dataSourceOptions);
		var dialect = DetectDialect.dialect(dataSourceFactory.getDateSource());
		this.parserFactory = new ParserFactory(dialect);
		var rowMapperFactory = new RowMapperFactory(parserFactory);
		var connectionFactory = new ConnectionFactory(dataSourceFactory.getDateSource(), rowMapperFactory);
		this.jdbcOperation = new DefaultJdbcOperation(parserFactory, connectionFactory);

        this.parseClazz();
		var tableChecker = new TableChecker(dataSourceFactory.getDateSource(), parserFactory.getEntityConfigCache());
		tableChecker.check();

		this.componentFactory = new ComponentFactory(parserFactory, jdbcOperation);
		DefaultJdbc jdbc = new DefaultJdbc(this);
		return jdbc;
	}

	@Override
	public JdbcBuilder setDataSourceOptions(DataSourceOptions options) {
		this.dataSourceOptions = options;
		return this;
	}

	@Override
	public JdbcBuilder addClazz(Class<?>... clazzes) {
		for (var clazz : clazzes) {
			if (!clazzList.contains(clazz)) {
				clazzList.add(clazz);
			}
		}
		return this;
	}

	@Override
	public JdbcBuilder addScanPackages(String... pkgs) {
		for (var pkg : pkgs) {
			if (!pkgList.contains(pkg)) {
				pkgList.add(pkg);
			}
		}
		return this;
	}

    private void parseClazz() {
        for (var clazz : clazzList) {
	        parserFactory.parse(clazz);
        }
        ClassScanner scanner = ClassScanner.create(c -> parserFactory.parse(c));
        scanner.scan(pkgList);
    }

	public DataSourceFactory getDataSourceFactory() {
		return dataSourceFactory;
	}

	public JdbcOperation getJdbcOperation() {
		return jdbcOperation;
	}

	public ComponentFactory getComponentFactory() {
		return componentFactory;
	}
}
